﻿namespace VisualPhysics.Scene.Core.Tests

open System
open System.Drawing
open VisualPhysics.Scene.Core
open VisualPhysics.Scene.Core.Operators
open VisualPhysics.Scene.Core.Tests.Utils
open NUnit.Framework
open FsUnit

type [<TestFixture>] ``RopeObject tests``() =
    let o1 = BallObject(pol (10.0<cm>, 10.0<cm>), 2.0<cm>, 3.0<gram>)
    let o2 = BallObject(pol (15.0<cm>, 15.0<cm>), 3.0<cm>, 5.0<gram>)
    let o3 = BallObject(pol (15.0<cm>, 10.0<cm>), 2.0<cm>, 3.0<gram>)

    [<Test>]
    [<Category("RopeObject.ValueMembers")>]
    member t.``Checking the Tag member of a RopeObject``() =
        let ``The rope we are testing`` = RopeObject(o1, o2, 1.0<gram>)
        let ``The rope we are testing as Object`` = ``The rope we are testing``.Object()
        ``The rope we are testing as Object``.Tag |> should equal Rope

    [<Test>]
    [<Category("RopeObject.ValueMembers")>]
    member t.``Checking the Position member of a RopeObject``() =
        let ``The rope we are testing`` = RopeObject(o1, o2, 1.0<gram>)
        let ``The rope we are testing as Object`` = ``The rope we are testing``.Object()
        ``The rope we are testing as Object``.CarPosition
        |> equalTuples (10.0<cm>, 10.0<cm>)
        |> should equal (10.0<cm>, 10.0<cm>)

    [<Test>]
    [<Category("RopeObject.ValueMembers")>]
    member t.``Checking the Color member of a RopeObject``() =
        let ``The rope we are testing`` = RopeObject(o1, o2, 1.0<gram>)
        let ``The rope we are testing as Object`` = ``The rope we are testing``.Object()
        ``The rope we are testing as Object``.Color |> should equal Brushes.Black

    [<Test>]
    [<Category("RopeObject.ValueMembers")>]
    member t.``Checking the Mass member of a RopeObject``() =
        let ``The rope we are testing`` = RopeObject(o1, o2, 1.0<gram>)
        let ``The rope we are testing as Object`` = ``The rope we are testing``.Object()
        ``The rope we are testing as Object``.Mass |> should equal 1.0<gram>
        
    [<Test>]
    [<Category("RopeObject.Setters")>]
    member t.``Changing the position of a RopeObject as Object``() =
        let ``The rope we are testing`` = RopeObject(o1, o2, 1.0<gram>)
        let ``The rope we are testing as Object`` = ``The rope we are testing``.Object()
        (fun () -> ``The rope we are testing as Object``.SetCarPosition (4.0<cm>, 6.0<cm>) |> ignore) |> should throw typeof<Exception>

    [<Test>]
    [<Category("RopeObject.Setters")>]
    member t.``Changing the color of a RopeObject as Object``() =
        let ``The rope we are testing`` = RopeObject(o1, o2, 1.0<gram>)
        let ``The rope we are testing as Object`` = ``The rope we are testing``.Object()
        let ``The new rope`` = ``The rope we are testing as Object``.SetColor Brushes.Orange
        ``The new rope``.Color |> should equal Brushes.Orange

    [<Test>]
    [<Category("RopeObject.SimultaneousCreators")>]
    member t.``Impossible creation - o1 and o2, l1 = 1, l2 = 2``() =
        let ``List of data`` = [(o1.Object(), Some(1.0<cm>), 1.0<gram>, None); (o2.Object(), Some(2.0<cm>), 1.0<gram>, None)]
        let Result = RopeObject.Create ``List of data`` 1.3<s>
        Result |> should equal None

    [<Test>]
    [<Category("RopeObject.SimultaneousCreators")>]
    member t.``Possible creation - o1 and o2, l1 = 4, l2 = None. Checking hook position.``() =
        let ``List of data`` = [(o1.Object(), Some(4.0<cm>), 1.0<gram>, None); (o2.Object(), None, 1.0<gram>, None)]
        let Result = RopeObject.Create ``List of data`` 1.3<s>
        match Result with
        | Some(_, hook) ->
            hook.Object().CarPosition |> should equal (o1.Object().X + 2.0<cm> * sqrt 2.0, o1.Object().Y + 2.0<cm> * sqrt 2.0)
        | _ ->
            Assert.Fail("Creation failed.")

    [<Test>]
    [<Category("RopeObject.SimultaneousCreators")>]
    member t.``Possible creation - o1 and o2, l1 = 4, l2 = None. Checking second rope length.``() =
        let ``List of data`` = [(o1.Object(), Some(4.0<cm>), 1.0<gram>, None); (o2.Object(), None, 1.0<gram>, None)]
        let Result = RopeObject.Create ``List of data`` 1.3<s>
        match Result with
        | Some([_;o], _) ->
            o.Length |> should equal ((sqrt 50.0<cm ^ 2>) - 4.0<cm>)
        | _ ->
            Assert.Fail("Creation failed.")

    [<Test>]
    [<Category("RopeObject.SimultaneousCreators")>]
    member t.``Possible creation - o1, o2 and o3, l1 = 4, l2 = 4 and l3 = None. Checking hook position.``() =
        let o1, o2 = o1.Object(), o2.Object()
        let lA, lB = 4.0<cm>, 4.0<cm>
        let distanceBetweenCores = distance o1.CarPosition o2.CarPosition
        let centerLineTeta = asin2 (o2.Y - o1.Y) distanceBetweenCores
        let relativeTeta = acos2 (lB * lB + distanceBetweenCores * distanceBetweenCores - lA * lA) (2.0 * lB * distanceBetweenCores)
        let teta = centerLineTeta + relativeTeta
        let is = lA * cos teta + o1.X, lA * sin teta + o1.Y

        let ``List of data`` = [(o1, Some(4.0<cm>), 1.0<gram>, None); (o2, Some(4.0<cm>), 1.0<gram>, None);
                                (o3.Object(), None, 1.0<gram>, None)]
        let Result = RopeObject.Create ``List of data`` 1.3<s>
        match Result with
        | Some(_, hook) ->
            hook.Object().CarPosition
            |> equalTuples is
            |> should equal is
        | _ ->
            Assert.Fail("Creation failed.")